package ms.sudoku.technique;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import ms.sudoku.PuzzleState;
import ms.sudoku.util.IntSet;

import org.lex.game.sudoku.model.Point;

//Implements the Sudoku block and column/row interaction technique.
public final class BlockAndColumnRowInteractionTechnique extends EliminationTechnique {
	public BlockAndColumnRowInteractionTechnique() {
	}

	public long getDifficultyLevel() {
		return 4;
	}

	public boolean execute(PuzzleState state, boolean exitEarlyWhenSoleFound, IntSet[][] possibleNumbers,
			AtomicInteger numberOfChanges, AtomicBoolean exitedEarly) {
		numberOfChanges.set(0);
		exitedEarly.set(false);

		int gridSize = state.getGridSize();
		int boxSize = state.getBoxSize();

		// Find the open cells in this block
		int numCount = 0;
		Point[] foundLocations = new Point[gridSize];
		// Analyze each box
		for (int box = 0; box < gridSize; box++) {
			for (int n = 0; n < gridSize; n++) {
				numCount = 0;
				// Find all locations that n is possible within the box
				int boxStartX = (box / boxSize) * boxSize;
				for (int row = boxStartX; row < boxStartX + boxSize && numCount <= boxSize; row++) {
					int boxStartY = (box % boxSize) * boxSize;
					for (int col = boxStartY; col < boxStartY + boxSize && numCount <= boxSize; col++) {
						if (possibleNumbers[row][col].contains(n)) {
							foundLocations[numCount++] = new Point(row, col);
						}

					}
				}

				/*
				 * Matters only when two or three are open in the grid and if
				 * they're in the same row or column
				 */
				if (numCount > 1 && numCount <= boxSize) {
					boolean matchesRow = true, matchesColumn = true;
					int row = foundLocations[0].x;
					int column = foundLocations[0].y;
					for (int i = 1; i < numCount; i++) {
						if (foundLocations[i].x != row)
							matchesRow = false;

						if (foundLocations[i].y != column)
							matchesColumn = false;

					}
					// If they're all in the same row
					if (matchesRow) {
						for (Integer j = 0; j < gridSize; j++) {
							// only works if BoxSize == 3
							if (possibleNumbers[row][j].contains(n) && j != foundLocations[0].y
									&& j != foundLocations[1].y && (numCount == 2 || j != foundLocations[2].y)) {
								possibleNumbers[row][j].remove(n);
								numberOfChanges.incrementAndGet();
								if (exitEarlyWhenSoleFound && possibleNumbers[row][j].size() == 1) {
									exitedEarly.set(true);
									return false;
								}

							}

						}
					} else if (matchesColumn) {
						// If they're all in the same column
						for (Integer j = 0; j < gridSize; j++) {
							// only works if BoxSize == 3
							if (possibleNumbers[j][column].contains(n) && j != foundLocations[0].x
									&& j != foundLocations[1].x && (numCount == 2 || j != foundLocations[2].x)) {
								possibleNumbers[j][column].remove(n);
								numberOfChanges.incrementAndGet();
								if (exitEarlyWhenSoleFound && possibleNumbers[j][column].size() == 1) {
									exitedEarly.set(true);
									return false;
								}
							}
						}
					}
				}
			}
		}
		return numberOfChanges.get() != 0;
	}

}
